package persistencia;

import gamebook.Administrador;
import gamebook.Asociado;
import gamebook.Usuario;
import gamebook.UsuarioGenerico;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class PUsuarioGenerico {

    /**
     * Introduce un nuevo usuario en la base de datos independientemente de qué
     * tipo de usuario sea. El valor devuelto indica el éxito de la operación
     * @param usuarioGenerico usuario que queremos guardar
     * @return true o false dependiendo de si se guardó bien o no
     */
    public static boolean darAltaBD(UsuarioGenerico usuarioGenerico) {
        boolean resultado = true;         //Si resultado es false es que hubo algún problema
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        //Para el uso de fechas
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

        try {
            //Conexión con la base de datos
            connection = Persistencia_Conexion.getConnection();
            statement = connection.createStatement();

            //El campo de fecha de nacimiento necesita un tratamiento previo para pasarlo a String
            Date dateNacimiento = usuarioGenerico.getNacimiento();
            String stringNacimiento = dateFormat.format(dateNacimiento);

            //El tipo se obtiene del tipo de clase a la que pertenece el usuario
            int tipo = 0;
            if (usuarioGenerico instanceof Usuario) {
                tipo = 1;
            } else if (usuarioGenerico instanceof Asociado) {
                tipo = 2;
            } else if (usuarioGenerico instanceof Administrador) {
                tipo = 3;
            }

            String sql="insert into usuarios ("
                    + "nombre,"
                    + "apellidos,"
                    + "mail,"
                    + "nacimiento,"
                    + "sexo,"
                    + "password,"
                    + "localidad,"
                    + "tipo,"
                    + "activo"
                    + ") values ("
                    + "'" + usuarioGenerico.getNombre() + "',"
                    + "'" + usuarioGenerico.getApellidos() + "',"
                    + "'" + usuarioGenerico.getMail() + "',"
                    + "'" + stringNacimiento + "',"
                    + "'" + usuarioGenerico.getSexo() + "',"
                    + "'" + usuarioGenerico.getPassword() + "',"
                    + "'" + usuarioGenerico.getLocalidad() + "',"
                    + tipo + ","
                    + "1"
                    + ")";
            
            //Pasamos el INSERT a la base de datos
            int registros = statement.executeUpdate(sql);

            //Verificamos que se haya actualizado UN registro
            if (registros == 1) {
                //Si hemos introducido el nuevo usuario, le asignamos el ID con el que se guardó en la base de datos
                resultSet = statement.executeQuery("select id from usuarios order by id desc");
                if (resultSet.next()) {
                    usuarioGenerico.setId(resultSet.getInt("id"));
                }
            } else {
                //Si no se ha introducido el nuevo usuario por alguna causa, asignamos false a resultado
                resultado = false;
            }

        } catch (Exception ex) {
            //Si hubo error en algun sitio, ponemos resultado a false
            resultado = false;
        } finally {
            //Cerramos todo y salimos
            try {
                resultSet.close();
                statement.close();
                connection.close();
            } catch (Exception ex) {
            }
        }

        return resultado;
    }

    /**
     * Actualiza los datos de un usuario en la base de datos independientemente
     * de qué tipo de usuario sea. El valor devuelto indica el éxito de la
     * operación, pero esto puede ser por un error o porque los datos que hemos
     * intentado actualizar son iguales a los que había.
     * @param usuarioGenerico usuario que queremos actualizar
     * @return true o false dependiendo de si se cambio algo o no
     */
    public static boolean actualizarBD(UsuarioGenerico usuarioGenerico) {
        boolean resultado = true;         //Si resultado es false es que hubo algún problema
        Connection connection = null;
        Statement statement = null;

        //Para el uso de fechas
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

        try {
            //Conexión con la base de datos
            connection = Persistencia_Conexion.getConnection();
            statement = connection.createStatement();

            //El campo de fecha de nacimiento necesita un tratamiento previo para pasarlo a String
            Date dateNacimiento = usuarioGenerico.getNacimiento();
            String stringNacimiento = dateFormat.format(dateNacimiento);

            //El tipo se obtiene del tipo de clase a la que pertenece el usuario
            int tipo = 0;
            if (usuarioGenerico instanceof Usuario) {
                tipo = 1;
            } else if (usuarioGenerico instanceof Asociado) {
                tipo = 2;
            } else if (usuarioGenerico instanceof Administrador) {
                tipo = 3;
            }

            //Pasamos el UPDATE a la base de datos
            int registros = statement.executeUpdate("update usuarios set "
                    + "nombre='" + usuarioGenerico.getNombre() + "',"
                    + "apellidos='" + usuarioGenerico.getApellidos() + "',"
                    + "mail='" + usuarioGenerico.getMail() + "',"
                    + "nacimiento='" + stringNacimiento + "',"
                    + "sexo='" + usuarioGenerico.getSexo() + "',"
                    + "password='" + usuarioGenerico.getPassword() + "',"
                    + "localidad='" + usuarioGenerico.getLocalidad() + "',"
                    + "tipo=" + tipo + " "
                    + "where id=" + usuarioGenerico.getId());

            //Verificamos que se haya actualizado UN registro
            if (registros != 1) {
                //Esto puede ser por algún fallo o porque los valores introducidos son iguales a los que había
                resultado = false;
            }

        } catch (Exception ex) {
            //Si hubo error en algun sitio, ponemos resultado a false
            resultado = false;
        } finally {
            //Cerramos todo y salimos
            try {
                statement.close();
                connection.close();
            } catch (Exception ex) {
            }
        }

        return resultado;
    }

    /**
     * Obtiene de la base de datos al usuario del tipo que sea cuyo mail se
     * corresponda con el parámetro suministrado
     * @return UsuarioGenerico
     */
    public static UsuarioGenerico seleccionarPorMailBD(String mail) {
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();
        String sql = "select id, nombre, apellidos, mail, nacimiento, sexo, password, localidad, tipo, activo "
                + "from usuarios "
                + "where mail='" + mail + "' and activo=1";
        listUsuarioGenerico = seleccionarBD(sql);
        if (listUsuarioGenerico.isEmpty()) {
            return null;
        } else {
            return listUsuarioGenerico.get(0);
        }
    }

    /**
     * Devuelve el UsuarioGenerico que tiene como id el parámetro que le pasamos
     * al método. Si no existiese dicho usuario, la respuesta es un null.
     * @param id id del usuario requerido
     * @return Devuelve un UsuarioGenerico o null si no existe usuario con dicho id.
     */
    public static UsuarioGenerico seleccionarPorIdBD(int id) {
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();
        String sql = "select id, nombre, apellidos, mail, nacimiento, sexo, password, localidad, tipo, activo "
                + "from usuarios "
                + "where id=" + id + " and activo=1";
        listUsuarioGenerico = seleccionarBD(sql);
        if (listUsuarioGenerico.isEmpty()) {
            return null;
        } else {
            return listUsuarioGenerico.get(0);
        }
    }

    /**
     * Devuelve una lista de clientes cuyo nombre o apellido coincida con cierto patron
     * que le pasamos al método. Si la palabra es una cadena vacía devolvería TODOS los
     * usuarios.
     * @param nombre o apellido
     * @return List<UsuarioGenerico>
     */
    public static List<UsuarioGenerico> seleccionarPorNombreBD(UsuarioGenerico u, String nombre, int inicio, int cantidad) {
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();
        String sql = "select id, nombre, apellidos, mail, nacimiento, sexo, password, localidad, tipo, activo "
                + "from usuarios where (nombre like '%" + nombre + "%' or apellidos "
                + "like '%" + nombre + "%') and activo=1 and id<>" + u.getId() + " "
                + "ORDER BY apellidos asc "
                + "limit " + inicio + " , " + cantidad;
        listUsuarioGenerico = seleccionarBD(sql);
        return listUsuarioGenerico;
    }

    public static int cuentaPorNombreBD(UsuarioGenerico u, String nombre) throws SQLException {

        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        String sql = "select count(0) "
                + "from usuarios where (nombre like '%" + nombre + "%' or apellidos "
                + "like '%" + nombre + "%') and activo=1 and id<>" + u.getId() + " "
                + "ORDER BY apellidos asc ";

        resultSet = statement.executeQuery(sql);
        int retorno = 0;
        if (resultSet.next()) {
            retorno = resultSet.getInt("count(0)");
        }
        return retorno;
    }

    /**
     * Devuelve una lista de clientes cuyo nombre o apellido coincida con cierto patron
     * que le pasamos al método. La diferencia con el metodo anterior que solo devuelve
     * los amigos del usuario pasado por parametro
     * @param nombre o apellido
     * @param usuario
     * @return List<UsuarioGenerico>
     */
    public static List<UsuarioGenerico> seleccionarPorNombreBDAmigos(UsuarioGenerico u, String nombre, int inicio, int cantidad) {
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();
        String sql = "select DISTINCT usuarios.id, nombre, apellidos, mail, nacimiento, sexo, password, localidad, tipo, activo "
                + "from usuarios INNER JOIN amistades ON id_anfitrion = usuarios.id OR id_invitado = usuarios.id "
                + "where (nombre like '%" + nombre + "%' or apellidos "
                + "like '%" + nombre + "%') and activo=1 and usuarios.id<>" + u.getId() + "  and "
                + "confirmado=1 AND (id_anfitrion=" + u.getId() + " OR id_invitado=" + u.getId() + ") "
                + "ORDER BY apellidos asc "
                + "limit " + inicio + " , " + cantidad;
        listUsuarioGenerico = seleccionarBD(sql);
        return listUsuarioGenerico;
    }

    
    public static int cuentaPorNombreBDAmigos(UsuarioGenerico u, String nombre) throws SQLException {

        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        String sql = "select DISTINCT count(0) "
                + "from usuarios INNER JOIN amistades ON id_anfitrion = usuarios.id OR id_invitado = usuarios.id "
                + "where (nombre like '%" + nombre + "%' or apellidos "
                + "like '%" + nombre + "%') and activo=1 and usuarios.id<>" + u.getId() + "  and "
                + "confirmado=1 AND (id_anfitrion=" + u.getId() + " OR id_invitado=" + u.getId() + ") "
                + "ORDER BY apellidos asc ";

        resultSet = statement.executeQuery(sql);
        int retorno = 0;
        if (resultSet.next()) {
            retorno = resultSet.getInt("count(0)");
        }
        return retorno;
    }

    /**
     * Obtiene una lista de los usuarios que han invitado a un cierto usuario 
     * a ser amigos.
     * @param usuarioGenerico al que han invitado
     * @return una lista de usuarios que han invitado al usuario a ser amigos
     */
    public static List<UsuarioGenerico> seleccionarUsuariosGenericosQueInvitaronBD(UsuarioGenerico usuarioGenerico) {
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();
        String sql = "SELECT DISTINCT usuarios.id, nombre, apellidos, mail, nacimiento, sexo, password, localidad, tipo, activo "
                + "FROM usuarios INNER JOIN amistades ON id_anfitrion = usuarios.id "
                + "WHERE id_invitado =" + usuarioGenerico.getId()
                + " AND confirmado=0 "
                + "ORDER BY apellidos asc ";
        listUsuarioGenerico = seleccionarBD(sql);
        return listUsuarioGenerico;
    }

    /**
     * Obtiene una lista de usuarios genéricos según la consulta sql que pasemos
     * @return List<UsuarioGenerico>
     */
    public static List<UsuarioGenerico> seleccionarBD(String sql) {
        //Lista de UsuariosGenericos que devolveremos
        List<UsuarioGenerico> listUsuarioGenerico = new ArrayList();

        //Para conectar con la base de datos
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        //Para la fecha de nacimiento
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

        try {
            //Conectamos con la base de datos
            UsuarioGenerico usuarioGenerico = null;
            connection = Persistencia_Conexion.getConnection();
            statement = connection.createStatement();

            resultSet = statement.executeQuery(sql);

            //Mientras haya usuarios que cumplan el criterio SQL los vamos añadiendo
            while (resultSet.next()) {

                if (resultSet.getInt("tipo") == 1) { //Tipo 1 es un usuario normal
                    Usuario usuario = new Usuario();
                    //Creamos un usuario y metemos las cosas que sean exclusivas de usuario
                    //...no hay ninguna.
                    usuarioGenerico = usuario;
                } else if (resultSet.getInt("tipo") == 2) { //Tipo 2 es un usuario asociado
                    Asociado asociado = new Asociado();
                    //Creamos un asociado y metemos las cosas que sean exclusivas de asociado
                    //...no hay ninguna.
                    usuarioGenerico = asociado;
                } else if (resultSet.getInt("tipo") == 3) { //Tipo 3 es un administrador
                    Administrador administrador = new Administrador();
                    //Creamos un administrador y metemos las cosas que sean exclusivas de administrador
                    //...no hay ninguna.
                    usuarioGenerico = administrador;
                }

                //Ahora si metemos todo lo que sea común a las tres subclases
                usuarioGenerico.setId(resultSet.getInt("id"));
                usuarioGenerico.setNombre(resultSet.getString("nombre"));
                usuarioGenerico.setApellidos(resultSet.getString("apellidos"));
                usuarioGenerico.setMail(resultSet.getString("mail"));
                usuarioGenerico.setNacimiento(dateFormat.parse(resultSet.getString("nacimiento")));
                usuarioGenerico.setSexo(resultSet.getString("sexo"));
                usuarioGenerico.setPassword(resultSet.getString("password"));
                usuarioGenerico.setLocalidad(resultSet.getString("localidad"));

                //Agregamos a la lista y cogemos el siguiente si lo hay
                listUsuarioGenerico.add(usuarioGenerico);
            }

        } catch (Exception ex) {
            System.out.println(ex.toString());
        } finally {
            try {
                resultSet.close();
                statement.close();
                connection.close();
            } catch (Exception ex) {
                System.out.println(ex.toString());
            }
        }

        return listUsuarioGenerico;
    }

    /**
     * Devuelve un booleano que indica si u1 es amigo de u2
     * los amigos del usuario pasado por parametro
     * @return boolean
     */
    public static boolean esAmigo(UsuarioGenerico u1, UsuarioGenerico u2) throws SQLException {
        boolean devuelve = false;

        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        String query = "SELECT COUNT(0) FROM amistades WHERE confirmado=1 AND "
                + "((id_invitado =" + u1.getId() + " AND id_anfitrion =" + u2.getId() + ") "
                + "OR (id_invitado =" + u2.getId() + " AND id_anfitrion =" + u1.getId() + "))";

        resultSet = statement.executeQuery(query);

        if (resultSet.next()) {
            if (resultSet.getInt("COUNT(0)") == 1) { //Tipo 1 es un usuario normal
                devuelve = true;
            }
        }

        return devuelve;

    }

    public static boolean invitar(UsuarioGenerico uOrigen, UsuarioGenerico uDestino) throws SQLException {
        boolean devuelve = false;

        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        //Esta consulta me indica si hay algun registro en la tabla amistad entre el usuario que invita y el que lo recibe
        String query = "SELECT COUNT(0) FROM amistades WHERE "
                + "(id_invitado =" + uDestino.getId() + " AND id_anfitrion =" + uOrigen.getId() + ") "
                + "OR (id_invitado =" + uOrigen.getId() + " AND id_anfitrion =" + uDestino.getId() + ")";

        resultSet = statement.executeQuery(query);
        //Si hay una invitacion en curso no la hace, si no la hay la realiza
        //Se queda pendiente si se deniega una solicitud, si se bloquean nuevas invitaciones o no
        if (resultSet.next()) {
            if (resultSet.getInt("COUNT(0)") == 1) { //Hay una invitacion pendiente ya
                return devuelve;
            } else if (resultSet.getInt("COUNT(0)") == 0) { //No hay invitaciones previas
                query = "INSERT INTO amistades (id_anfitrion,id_invitado,confirmado) VALUES  "
                        + "(" + uOrigen.getId() + "," + uDestino.getId() + ",0)";
                statement.executeUpdate(query);
                devuelve = true;
            }
        }
        return devuelve;
    }

    public static void respuestaInvitacion(UsuarioGenerico uInvitador, UsuarioGenerico uInvitado, boolean acepta) throws SQLException {
        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        String query = "";
        if (acepta) {
            query = "UPDATE amistades SET confirmado=1 WHERE "
                    + "id_invitado =" + uInvitado.getId() + " AND id_anfitrion =" + uInvitador.getId() + "";
        } else {
            query = "DELETE FROM amistades WHERE "
                    + "id_invitado =" + uInvitado.getId() + " AND id_anfitrion =" + uInvitador.getId() + " AND confirmado=0";
        }
        statement.executeUpdate(query);
    }

    public static void borraAmigo(UsuarioGenerico usuarioSesion, UsuarioGenerico usuarioEliminado) throws SQLException, Exception {
        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        String query = query = "DELETE FROM amistades WHERE "
                + "(id_invitado =" + usuarioSesion.getId() + " AND id_anfitrion =" + usuarioEliminado.getId() + ") OR "
                + "(id_invitado =" + usuarioEliminado.getId() + " AND id_anfitrion =" + usuarioSesion.getId() + ") AND confirmado=1";
        if (statement.executeUpdate(query) == 0) {
            throw new Exception("No se ha eliminado ningun registro");
        }


    }

    public static int numeroNuevasInvitaciones(UsuarioGenerico usuario) throws SQLException {
        int numeroInvitaciones = 0;
        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        String query = "Select count(0) as invitaciones FROM amistades "
                + "WHERE id_invitado = " + usuario.getId() + " AND confirmado=0";

        resultSet = statement.executeQuery(query);
        resultSet.next();
        return numeroInvitaciones = resultSet.getInt("invitaciones");

    }

    //Obtiene de la ultima invitacion que ha recibido, el id del usuario que la realizó
    public static int getIDPersonaQueInvita(UsuarioGenerico usuario) throws SQLException {
        int idInvitador = 0;

        Connection connection = Persistencia_Conexion.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = null;

        String query = "Select id_anfitrion FROM amistades "
                + "WHERE id_invitado = " + usuario.getId() + " AND confirmado=0 "
                + "ORDER BY id desc";

        resultSet = statement.executeQuery(query);
        if (resultSet.next()) {
            idInvitador = resultSet.getInt("id_anfitrion");
        }

        return idInvitador;
    }
}
